package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"slices"
	"strconv"
	"strings"
	"time"
)

func main() {
	mux := http.NewServeMux()
	// 静态页面
	mux.Handle("/", http.FileServer(http.Dir("./static")))
	mux.HandleFunc("/api/", requestHandler)

	// API路由
	//mux.HandleFunc("/api/login", loginHandler)

	//// Device相关API
	//mux.HandleFunc("/api/devices", getDevicesHandler)
	//
	//mux.HandleFunc("/api/static/bind", staticHandler)
	//
	//// 绑定设备API
	//mux.HandleFunc("/api/bindings/list", getBindingsHandler)
	//mux.HandleFunc("/api/bindings/save", saveBindingHandler)
	//mux.HandleFunc("/api/bindings/delete", deleteBindingHandler)
	//
	//// PassWall相关API
	//mux.HandleFunc("/api/passwall/list", getNodesHandler)
	//mux.HandleFunc("/api/passwall/save", saveNodeHandler)
	//mux.HandleFunc("/api/passwall/delete", deleteNodeHandler)

	// 启动服务器
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	server := &http.Server{
		Addr:         ":" + port,
		Handler:      mux,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  60 * time.Second,
	}

	log.Printf("Server running on :%s", port)
	log.Fatal(server.ListenAndServe())
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	type LoginRequest struct {
		Password string `json:"password"`
	}
	var req LoginRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "无效请求", http.StatusBadRequest)
		return
	}

	ok := req.Password == adminPassword
	if !ok {
		loginFailedCount++
		if loginFailedCount >= 5 {
			http.Error(w, "失败次数上限", http.StatusBadRequest)
			time.AfterFunc(time.Minute*10, func() {
				loginFailedCount = 0
			})
			return
		}
	}

	token := CreateCode(20)
	loginTokens = append(loginTokens, &Token{token, time.Now().Add(time.Hour)})

	// 设置 Cookie
	http.SetCookie(w, &http.Cookie{
		Name:     "session_id",
		Value:    token,
		Path:     "/",
		HttpOnly: true,  // 防止 JS 读取
		Secure:   false, // 生产中建议启用 HTTPS 时设为 true
		MaxAge:   3600,
	})

	response := map[string]bool{"success": ok}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

func requestHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/api/login" {
		loginHandler(w, r)
		return
	}

	cookie, err := r.Cookie("session_id")
	if err != nil || cookie.Value == "" {
		http.Error(w, "未登录", http.StatusUnauthorized)
		return
	}

	if !TokenCheck(cookie.Value) {
		http.Error(w, "登录已失效", http.StatusUnauthorized)
		return
	}

	switch r.URL.Path {
	case "/api/devices":
		getDevicesHandler(w, r)
	case "/api/static/bind":
		staticHandler(w, r)
	case "/api/bindings/list":
		getBindingsHandler(w, r)
	case "/api/bindings/save":
		saveBindingHandler(w, r)
	case "/api/bindings/delete":
		deleteBindingHandler(w, r)
	case "/api/passwall/list":
		getNodesHandler(w, r)
	case "/api/passwall/save":
		saveNodeHandler(w, r)
	case "/api/passwall/delete":
		deleteNodeHandler(w, r)
	}
}

func staticHandler(w http.ResponseWriter, r *http.Request) {
	var param struct {
		Mac  string `json:"mac"`
		IP   string `json:"ip"`
		Name string `json:"name"`
	}
	if err := json.NewDecoder(r.Body).Decode(&param); err != nil || param.Mac == "" {
		http.Error(w, "无效参数", http.StatusBadRequest)
		return
	}

	passwallBody, _ := os.ReadFile(passwallConfig)
	if strings.Contains(string(passwallBody), param.Mac) && param.IP == "" {
		http.Error(w, "解绑失败", http.StatusBadRequest)
		return
	}

	dhcpData, err := getConfig(dhcpConfig)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	arpData, err := getConfig(arpbindConfig)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	dhcpBody, _ := os.ReadFile(dhcpConfig)
	arpBody, _ := os.ReadFile(arpbindConfig)

	// 去掉绑定
	for _, row := range dhcpData {
		if mac, ok := row.Data["mac"]; ok && mac == param.Mac {
			dhcpBody = bytes.ReplaceAll(dhcpBody, []byte(row.Code), nil)
		}
	}
	for _, row := range arpData {
		if mac, ok := row.Data["macaddr"]; ok && mac == param.Mac {
			arpBody = bytes.ReplaceAll(arpBody, []byte(row.Code), nil)
		}
	}
	os.WriteFile(dhcpConfig, dhcpBody, 0644)
	os.WriteFile(arpbindConfig, arpBody, 0644)

	if param.IP != "" {
		// 添加绑定
		err = bindingStatic(Device{Name: param.Name, IP: param.IP, MAC: param.Mac})
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
	} else {
		exec.Command("/etc/init.d/dnsmasq", "restart").Run()
	}

	cacheCleanup()

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]bool{"success": true})
}

func getDevicesHandler(w http.ResponseWriter, r *http.Request) {
	// 获取设备列表
	devices, err := getDevices()
	if err != nil {
		http.Error(w, "Failed to get devices", http.StatusInternalServerError)
		return
	}

	// 返回分页结果
	response := PaginatedResponse{
		Data: devices,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

func deleteBindingHandler(w http.ResponseWriter, r *http.Request) {
	macs := strings.Split(r.URL.Query().Get("mac"), ",")

	// 这里应该是删除静态绑定的实际逻辑
	if err := deleteBinding(macs...); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	cacheCleanup()

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]bool{"success": true})
}

func getNodesHandler(w http.ResponseWriter, r *http.Request) {
	// 获取节点列表
	nodes, err := getNodes()
	if err != nil {
		http.Error(w, "Failed to get nodes", http.StatusInternalServerError)
		return
	}

	// 返回分页结果
	response := PaginatedResponse{
		Data: nodes,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

func getNodes() ([]Node, error) {
	data, err := getConfig(passwallConfig)
	if err != nil {
		return nil, err
	}

	devices, _ := getDevices()

	var nodes []Node
	for _, block := range data {
		var node Node
		isSock5 := false
		for key, value := range block.Data {
			switch key {
			case "nodes":
				node.ID = value
			case "password":
				node.Password = value
			case "port":
				node.Port, _ = strconv.Atoi(value)
			case "type":
				if strings.ToLower(value) == "socks" {
					isSock5 = true
				}
			case "remarks":
				node.Name = value
			case "address":
				node.Address = value
			case "username":
				node.Username = value
			}
		}
		if isSock5 {
			for _, b := range data {
				if _, ok := b.Data["acl_rule"]; !ok {
					continue
				}
				if id, ok := b.Data["tcp_node"]; ok && id == node.ID {
					source, ok := b.Data["sources"]
					if !ok {
						continue
					}
					for _, device := range devices {
						if strings.Contains(source, device.MAC) {
							node.Devices = append(node.Devices, device)
						}
					}
				}
			}
			nodes = append(nodes, node)
		}
	}
	return nodes, nil
}

func saveNodeHandler(w http.ResponseWriter, r *http.Request) {
	var nodes []Node
	if err := json.NewDecoder(r.Body).Decode(&nodes); err != nil {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	// 这里应该是添加节点的实际逻辑
	if err := saveNode(nodes...); err != nil {
		http.Error(w, "Failed to add node", http.StatusInternalServerError)
		return
	}

	cacheCleanup()

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]bool{"success": true})
}

func saveNode(nodes ...Node) error {
	if len(nodes) == 0 {
		return errors.New("参数错误")
	}

	data, err := getConfig(passwallConfig)
	if err != nil {
		return err
	}

	body, _ := os.ReadFile(passwallConfig)
	for _, node := range nodes {
		if node.Name == "" || node.Address == "" || node.Port == 0 {
			return errors.New("参数错误")
		}

		code := `
config nodes '` + CreateCode(10) + `'`

		var block BufferBlock
		for _, row := range data {
			if nodeId, ok := row.Data["nodes"]; ok && nodeId == node.ID {
				code = `
config nodes '` + nodeId + `'`
				block = row
			}
		}

		if node.Username == "" {
			code += fmt.Sprintf(`
        option port '%d'
        option type 'Socks'
        option add_mode '1'
        option remarks '%d'
        option address '%s'
`, node.Port, node.Name, node.Address)
		} else {
			code += fmt.Sprintf(`
        option password '%s'
        option port '%d'
        option type 'Socks'
        option add_mode '1'
        option remarks '%s'
        option address '%s'
        option username '%s'
`, node.Password, node.Port, node.Name, node.Address, node.Username)
		}

		if block.Code == "" {
			// 添加
			body = append(body, code...)
		} else {
			// 修改
			body = bytes.ReplaceAll(body, []byte(block.Code), []byte(code))
		}
	}
	os.WriteFile(passwallConfig, body, 0644)

	exec.Command("/etc/init.d/passwall", "restart").Run()
	return nil
}

func deleteNodeHandler(w http.ResponseWriter, r *http.Request) {
	nodeIds := strings.Split(r.URL.Query().Get("id"), ",")

	// 这里应该是删除静态绑定的实际逻辑
	if err := deleteNode(nodeIds...); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	cacheCleanup()

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]bool{"success": true})
}

func deleteNode(nodeIds ...string) error {
	if len(nodeIds) == 0 {
		return errors.New("待删除节点不能为空")
	}

	data, err := getConfig(passwallConfig)
	if err != nil {
		return err
	}

	body, _ := os.ReadFile(passwallConfig)
	for _, row := range data {
		if _, ok := row.Data["acl_rule"]; ok {
			// TODO: 校验节点是否被使用
			if nodeId, ok := row.Data["tcp_node"]; ok && slices.Contains(nodeIds, nodeId) {
				return fmt.Errorf(`节点[%s]已被绑定，无法删除`, nodeId)
			}
		}
		if nodeId, ok := row.Data["nodes"]; ok && slices.Contains(nodeIds, nodeId) {
			// 删除节点
			body = bytes.ReplaceAll(body, []byte(row.Code), nil)
		}
	}
	os.WriteFile(passwallConfig, body, 0644)
	exec.Command("/etc/init.d/passwall", "restart").Run()
	return nil
}

func getBindingsHandler(w http.ResponseWriter, r *http.Request) {
	// 获取绑定列表
	bindings, err := getBindings()
	if err != nil {
		http.Error(w, "Failed to get bindings", http.StatusInternalServerError)
		return
	}

	// 返回分页结果
	response := PaginatedResponse{
		Data: bindings,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

func getBindings() ([]Binding, error) {
	data, err := getConfig(passwallConfig)
	if err != nil {
		return nil, err
	}
	devices, err := getDevices()
	if err != nil {
		return nil, err
	}
	var bindings []Binding
	for _, block := range data {
		var binding Binding
		isRule := false
		for key, value := range block.Data {
			switch key {
			case "acl_rule":
				isRule = true
				binding.ID = value
				if binding.ID == "" {
					binding.ID = CreateCode(10)
				}
			case "enabled":
				binding.Status = value == "1"
			case "remarks":
				binding.Remarks = value
			case "tcp_node":
				binding.NodeID = value
				for _, b := range data {
					if nodeId, ok := b.Data["nodes"]; ok && nodeId == binding.NodeID {
						if v, ok := b.Data["remarks"]; ok {
							binding.NodeName = v
						}
					}
				}
			case "sources":
				binding.DeviceName = "*"
				for _, device := range devices {
					if slices.Contains(strings.Fields(value), device.MAC) {
						binding.DeviceMAC = device.MAC
						binding.DeviceIP = device.IP
						binding.DeviceName = device.Name
						break
					}
				}
				if binding.DeviceMAC == "" {
					for _, field := range strings.Fields(value) {
						if strings.Contains(field, ":") {
							binding.DeviceMAC = field
						} else if strings.Contains(field, ".") {
							binding.DeviceIP = field
						}
					}
				}
			}
		}
		if isRule {
			bindings = append(bindings, binding)
		}
	}
	return bindings, nil
}

func saveBindingHandler(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Name   string `json:"name"`
		Mac    string `json:"mac"`
		NodeID string `json:"nodeId"`
		Status bool   `json:"status"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request:"+err.Error(), http.StatusBadRequest)
		return
	}

	if err := saveBinding(req.Name, req.Mac, req.NodeID, req.Status); err != nil {
		http.Error(w, "Failed to add binding:"+err.Error(), http.StatusInternalServerError)
		return
	}

	cacheCleanup()

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]bool{"success": true})
}

func saveBinding(name, mac, nodeID string, status bool) error {
	devices, err := getDevices()
	if err != nil {
		return err
	}

	// 判断设备是否被静态绑定
	var device Device
	for _, d := range devices {
		if d.MAC == mac {
			device = d
		}
	}
	if device.MAC == "" {
		return errors.New("设备不存在")
	}
	if device.Name != name {
		device.Name = name
	}

	// 静态绑定
	err = bindingStatic(device)
	if err != nil {
		return err
	}

	nodes, err := getNodes()
	if err != nil {
		return err
	}
	var node Node
	for _, row := range nodes {
		if row.ID == nodeID {
			node = row
		}
	}
	if node.ID == "" {
		return errors.New("代理节点不存在")
	}

	err = bindingNode(name, mac, node, status)
	if err != nil {
		return err
	}

	// 并重启passwall服务
	exec.Command("/etc/init.d/passwall", "restart").Run()
	return nil
}

func bindingStatic(device Device) error {
	data, err := getConfig(dhcpConfig)
	if err != nil {
		return err
	}

	arpData, err := getConfig(arpbindConfig)
	if err != nil {
		return err
	}

	var block BufferBlock
	for _, row := range data {
		if _, ok := row.Data["host"]; !ok {
			continue
		}
		if mac, ok := row.Data["mac"]; ok && mac == device.MAC {
			block = row
			break
		}
	}

	blockCode := `
config host
        option mac '` + device.MAC + `'
        option ip '` + device.IP + `'
        option name '` + device.Name + `'
`
	body, _ := os.ReadFile(dhcpConfig)
	if block.Code != "" {
		// 修改
		body = bytes.ReplaceAll(body, []byte(block.Code), []byte(blockCode))
	} else {
		// 添加
		body = append(body, blockCode...)
	}
	os.WriteFile(dhcpConfig, body, 0644)

	arpCode := `
config arpbind
        option ifname 'br-lan'
        option ipaddr '` + device.IP + `'
        option macaddr '` + device.MAC + `'
`

	var arp BufferBlock
	for _, row := range arpData {
		if _, ok := row.Data["arpbind"]; !ok {
			continue
		}
		if mac, ok := row.Data["macaddr"]; ok && mac == device.MAC {
			arp = row
			break
		}
	}

	body, _ = os.ReadFile(arpbindConfig)
	if arp.Code != "" {
		// 修改
		body = bytes.ReplaceAll(body, []byte(arp.Code), []byte(arpCode))
	} else {
		// 添加
		body = append(body, arpCode...)
	}
	os.WriteFile(arpbindConfig, body, 0644)

	exec.Command("/etc/init.d/dnsmasq", "restart").Run()

	cacheCleanup()
	return nil
}

func bindingNode(name, mac string, node Node, status bool) error {
	data, err := getConfig(passwallConfig)
	if err != nil {
		return err
	}

	var block BufferBlock
	for _, row := range data {
		if _, ok := row.Data["acl_rule"]; !ok {
			continue
		}
		if source, ok := row.Data["sources"]; ok && strings.Contains(source, mac) {
			block = row
		}
	}

	enable := 1
	if !status {
		enable = 0
	}

	body, _ := os.ReadFile(passwallConfig)
	code := fmt.Sprintf(`
config acl_rule %s
        option enabled '%d'
        option remarks '%s'
        option interface 'lan'
        option use_global_config '0'
        option tcp_node '%s'
        option udp_node 'tcp'
        option tcp_proxy_drop_ports 'disable'
        option udp_proxy_drop_ports 'disable'
        option tcp_redir_ports '1:65535'
        option udp_redir_ports '1:65535'
        option use_gfw_list '0'
        option chn_list '0'
        option tcp_proxy_mode 'proxy'
        option udp_proxy_mode 'proxy'
        option dns_shunt 'dnsmasq'
        option dns_mode 'dns2socks'
        option remote_dns '114.114.114.114'
        option sources '%s'
`, CreateCode(10), enable, name, node.ID, mac)

	if block.Code == "" {
		// 添加
		body = append(body, code...)
	} else {
		// 修改
		body = bytes.ReplaceAll(body, []byte(block.Code), []byte(code))
	}
	os.WriteFile(passwallConfig, body, 0644)

	return nil
}

func deleteBinding(macs ...string) error {
	data, err := getConfig(passwallConfig)
	if err != nil {
		return err
	}

	body, _ := os.ReadFile(passwallConfig)
	for _, row := range data {
		if _, ok := row.Data["acl_rule"]; !ok {
			continue
		}
		source, ok := row.Data["sources"]
		if !ok {
			continue
		}
		for _, mac := range macs {
			if strings.Contains(source, mac) {
				body = bytes.ReplaceAll(body, []byte(row.Code), nil)
			}
		}
	}
	os.WriteFile(passwallConfig, body, 0644)

	exec.Command("/etc/init.d/passwall", "restart").Run()
	return nil
}
